iT邦幫忙

2026 iThome 鐵人賽

DAY 1
1
Kubernetes

凌晨四點,女友帶著 GPU 來我家學習 Kubernetes:打造 K8s AI Infra 的 30 夜系列 第 1

【Day 1】到底有什麼毛病啊,凌晨四點不睡覺學習 Kubernetes AI Infra

  • 分享至 

  • xImage
  •  

前言

凌晨四點,多數人還沉浸於夢鄉之中,儘管未到破曉之時,相信已經有許多人悄悄睜開雙眼,享受這靜謐的城市。

凌晨四點,或許派大星剛享用完他的美味蟹堡,或許哲哲正為他的粉絲寫著解盤文章,或許已經有人開始準備為一整天的生活打拼,也或許有人正等著他的另一半來他家煮一碗熱騰騰的牛肉麵,開啟一個美好的早晨。

但我相信,什麼都比不上凌晨四點請女友帶著 GPU 來我家,一起鑽研 Kubernetes 與 GPU 到底能產生什麼樣的化學反應。

嗯廢話有點多,所以為什麼會有這個系列呢?簡而言之,前陣子 Kubernetes v1.34 發布後 DRA 正式進入 GA,稍微玩了一下覺得這東西蠻酷的,至於什麼是 DRA 呢?想像一下在 K8s 上你的 Pod 能用類似 PV & PVC 的方式對 GPU 提交更彈性的申請,而不是以往的全有或全無,大概就是這樣,細節之後再說。

有了 DRA 後,那就更不能錯過 Kubernetes 與 AI Infra 的愛恨交織了,我作為一名 data engineer 應該對大部分 K8s 的觀念算是蠻熟悉的,但要牽扯到一些 LLM 相關的知識及原理就有一點力不從心了,於是就趁著這次鐵人賽的機會來自主研究一下 K8s 上的 AI Infra。


未來三十天

未來三十天的內容主要會去了解一些 LLM 相關觀念及技術名詞,並且實際去看這些技術是如何部署在 Kubernetes 上,以及 Kubernetes 原生提供了哪些 API resource 來解決哪些痛點。應該是沒辦法做到那種系統式的教學,整體比較會偏向是我自己的個人筆記,而且會蠻仰賴 AI 的知識,所以如果有寫錯的話再麻煩指正。


根據 2026 年 1 月 CNCF 年度報告

CNCF 在 2026 年一月發布了 2025 年度雲原生調查,以《The Infrastructure of AI's Future》為主題,對 628 位受訪者進行調查。

在有 AI 工作負載的組織中,66% 已經在 Kubernetes 上運行生成式 AI 工作負載。其中 23% 在推論工作負載上完全採用 Kubernetes,這些團隊可能已對模型部署導入 GitOps 工作流程、透過 Prometheus 與 Grafana 為模型效能指標建立穩固的監控機制,並將 AI 工作負載整合進既有的 CI/CD 流水線。另外 43% 則是部分採用,往往從批次推論或開發/測試環境切入,正式環境的服務仍維持既有的舊系統。

另有 18% 表示打算導入 Kubernetes,尚未動手的原因可能是既有 ML 平台的投資、對維運複雜度的疑慮,以及團隊需要重新訓練。畢竟把 AI 工作負載搬上 Kubernetes 不只是容器化那麼簡單,還得確保模型落在具備 GPU 親和性、資源配置適當的節點上,並為訓練流水線與低延遲服務分別設計不同的架構模式。

在 98% 的組織都已採用雲原生的情況下,焦點早就從「要不要用」轉移到「怎麼把價值最大化」。值得注意的是,52% 的組織並不訓練模型,只是消費模型,有在做的也大多只是拿自己的資料微調。然而即便只是消費,AI 的野心與部署現實之間的落差依然懸殊,47% 的組織只是偶爾部署模型,能做到每日部署的僅有 7%。

因此,當模型不是自己訓練的,平台大家都跑 Kubernetes,誰能打造完美的 AI Infra 便能拉開差距,如同該報告的結尾:

As cloud native becomes boring infrastructure, the competitive advantage shifts to those who can build reliable, scalable, and sustainable systems on that foundation.


GPU Operator

要讓 Kubernetes 用得到 GPU,光把卡插上去是不夠的。節點上得先有 NVIDIA driver 才跑得動 CUDA,要有 container runtime 才能把卡交進容器裡,要有 device plugin,kubelet 才認得到這張卡,還要有監控、要有節點標籤。這些東西一台一台手動裝,官方自己的說法是「difficult and prone to errors」。

GPU Operator 就是把這一整套自動化起來。它用 Kubernetes 的 operator framework 實作,一言以蔽之:「creates, configures, and manages GPUs in Kubernetes」。

它幫你管的東西包括:

元件 做什麼
NVIDIA drivers 讓節點跑得動 CUDA
NVIDIA Container Runtime 把 GPU 交進容器裡
Kubernetes device plugin 讓 kubelet 認得到這張卡
Automatic node labelling 自動標出這台機器有什麼硬體
DCGM-based monitoring GPU 的指標監控

https://ithelp.ithome.com.tw/upload/images/20260914/20183759IzcIRmJWvf.jpg


實驗環境

接下來三十天的實驗都會跑在 AWS EC2 上。考慮到經費限制,總共會啟動三台 EC2,其中一台沒有任何 GPU,會透過 Fake GPU Operator 在 Kubernetes API 層模擬 GPU 資源。另外兩台則各配置一張 NVIDIA L40S,用於單卡與雙節點情境的測試。

環境一:Fake GPU 節點

  • EC2 規格
執行個體 m7i.2xlarge
GPU
vCPU / 記憶體 8 vCPU / 32 GB
儲存 120 GB gp3
作業系統 Ubuntu 26.04 LTS

步驟 0:四樣工具

版本
Docker 29.1.3
kind v0.32.0
kubectl v1.36.4
helm v4.2.4
# --- Docker ---
sudo apt-get update && sudo apt-get install -y docker.io
sudo usermod -aG docker "$USER"
newgrp docker

# --- kind ---
curl -Lo /tmp/kind https://kind.sigs.k8s.io/dl/v0.32.0/kind-linux-amd64
sudo install -m 755 /tmp/kind /usr/local/bin/kind

# --- kubectl ---
curl -Lo /tmp/kubectl https://dl.k8s.io/release/v1.36.4/bin/linux/amd64/kubectl
sudo install -m 755 /tmp/kubectl /usr/local/bin/kubectl

# --- helm ---
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

步驟 1:建立 kind 叢集

fake-gpu-cluster.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: gpu-lab

featureGates:
  DRADeviceTaintRules: true
  DRAListTypeAttributes: true
  DRAWorkloadResourceClaims: true
  DRANodeAllocatableResources: true
  GangScheduling: true
  GenericWorkload: true

kubeadmConfigPatches:
  - |
    kind: ClusterConfiguration
    apiServer:
      extraArgs:
        - name: runtime-config
          value: "resource.k8s.io/v1beta1=true,resource.k8s.io/v1beta2=true,scheduling.k8s.io/v1alpha2=true"

containerdConfigPatches:
  - |-
    [plugins."io.containerd.grpc.v1.cri"]
      enable_cdi = true

nodes:
  - role: control-plane
    image: kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5
  - role: worker
    image: kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5
  - role: worker
    image: kindest/node:v1.36.1@sha256:3489c7674813ba5d8b1a9977baea8a6e553784dab7b84759d1014dbd78f7ebd5
kind create cluster --config fake-gpu-cluster.yaml --wait 120s

步驟 2:安裝 Fake GPU Operator

# ① 只標一個 worker 有 GPU,另一個保持乾淨,「有卡/沒卡」才對照得出來
kubectl --context kind-gpu-lab label node gpu-lab-worker \
  run.ai/simulated-gpu-node-pool=default --overwrite

# ② 裝 Fake GPU Operator
helm upgrade -i gpu-operator \
  oci://ghcr.io/run-ai/fake-gpu-operator/fake-gpu-operator \
  --kube-context kind-gpu-lab --namespace gpu-operator --create-namespace \
  --version 0.2.0 \
  --set-string topology.nodePools.default.gpuProduct="NVIDIA-A100-SXM4-40GB" \
  --set topology.nodePools.default.gpuCount=8 \
  --set topology.nodePools.default.gpuMemory=40960 \
  --wait --timeout 5m

裝完後可以來驗證一下:

kubectl --context kind-gpu-lab get nodes \
  -o custom-columns=NODE:.metadata.name,GPU:.status.allocatable.'nvidia\.com/gpu'

這台機器上沒有任何 GPU,但 Kubernetes 現在認為 gpu-lab-worker 插著八張 A100

NODE                    GPU
gpu-lab-control-plane   <none>
gpu-lab-worker          8
gpu-lab-worker2         <none>

環境二:實體 GPU 節點 - 兩台 EC2,組成一個 k3s 叢集

兩台規格完全相同,各一張 L40S。

  • EC2 規格
執行個體 g6e.2xlarge
GPU 1× NVIDIA L40S 48GB (allocatable 46068 MiB)
vCPU / 記憶體 8 vCPU / 64 GB
儲存 200 GB gp3
AMI Deep Learning Base OSS Nvidia Driver GPU AMI (Ubuntu 22.04)(驅動預裝)

下面步驟 0 兩台都要做,步驟 1 到 3 只在第一台,步驟 4 之後是第二台加入。

步驟 0:確認驅動,卡認得到(兩台都做)

nvidia-smi -L
GPU 0: NVIDIA L40S (UUID: GPU-f01809ac-8e3a-c964-f3f5-7cbd204b658a)

步驟 1:第一台建立 k3s 叢集

curl -sfL https://get.k3s.io | sudo sh -s - \
  --write-kubeconfig-mode 644 \
  --disable traefik --disable servicelb \
  --default-runtime nvidia

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

步驟 2:安裝 helm

curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

步驟 3:安裝 GPU Operator

在實體 GPU 的環境中,我們預設全部使用 DRA,並關閉 Device Plugin 的功能

helm upgrade --install gpu-operator \
  oci://nvcr.io/nvidia/cloud-native-charts/gpu-operator \
  --version v26.7.0 -n gpu-operator --create-namespace \
  --set driver.enabled=false \
  --set toolkit.enabled=false \
  --set gpuCluster.deployCR=true \
  --set clusterPolicy.deployCR=false \
  --set draDriver.featureGates.MPSSupport=true \
  --set draDriver.featureGates.TimeSlicingSettings=true \
  --wait --timeout 15m

確認 Pod 都 running

kubectl get pods -n gpu-operator
NAME                                                          READY   STATUS    RESTARTS   AGE
gpu-operator-6cd4764cbf-bwvr5                                 1/1     Running   0          38m
gpu-operator-node-feature-discovery-gc-7d4b864d68-6ch96       1/1     Running   0          38m
gpu-operator-node-feature-discovery-master-8649684bd6-lzcfv   1/1     Running   0          38m
gpu-operator-node-feature-discovery-worker-9ft75              1/1     Running   0          38m
nvidia-dcgm-exporter-dra-btcb7                                1/1     Running   0          37m
nvidia-dra-driver-controller-5499f7bdb-mq6bq                  1/1     Running   0          26m
nvidia-dra-driver-kubelet-plugin-s6hrt                        2/2     Running   0          26m
nvidia-dra-validator-t8g4w                                    1/1     Running   0          37m

步驟 4:第一台拿 token

sudo cat /var/lib/rancher/k3s/server/node-token

步驟 5:第二台以 agent 身分加入

第二台不用裝 k3s server,也不用再裝一次 helm 與 GPU Operator,GPU Operator 是叢集層級的,它的 DaemonSet 會自動鋪到新加入的節點上。

curl -sfL https://get.k3s.io -o /tmp/k3s-install.sh
sudo env \
  K3S_URL=https://<第一台的 private IP>:6443 \
  K3S_TOKEN=<token> \
  sh /tmp/k3s-install.sh --default-runtime nvidia

步驟 6:回第一台確認

kubectl get nodes -o wide
NAME              STATUS   ROLES           AGE     VERSION        INTERNAL-IP
ip-172-31-26-39   Ready    control-plane   6m19s   v1.36.4+k3s1   172.31.26.39
ip-172-31-30-5    Ready    <none>          58s     v1.36.4+k3s1   172.31.30.5

但節點 Ready 不代表卡被看見,真正的判準是兩張卡都出現在 ResourceSlice 裡:

kubectl get resourceslices -o wide
NAME                                                    NODE              DRIVER                      POOL
00000-compute-domain.nvidia.com-ip-172-31-26-39-gjkq9   ip-172-31-26-39   compute-domain.nvidia.com   ip-172-31-26-39
00000-compute-domain.nvidia.com-ip-172-31-30-5-xh29j    ip-172-31-30-5    compute-domain.nvidia.com   ip-172-31-30-5
00000-gpu.nvidia.com-ip-172-31-26-39-vlz4g              ip-172-31-26-39   gpu.nvidia.com              ip-172-31-26-39
00000-gpu.nvidia.com-ip-172-31-30-5-xgk8r               ip-172-31-30-5    gpu.nvidia.com              ip-172-31-30-5

小結

今天把接下來三十天要用的東西準備好了:一台沒有 GPU 的機器,靠 Fake GPU Operator 讓 Kubernetes 相信它插著八張 A100;另外兩台各一張 L40S,組成一個真正的兩節點叢集。

明天將從最基本的問題開始:GPU 上到底在跑什麼?一個請求送進去之後,又發生了什麼事。


參考資料

CNCF Annual Cloud Native Survey: The infrastructure of AI's future
[GitHub] Fake GPU Operator
[GitHub] GPU Operator
Building an AI factory on Kubernetes


系列文
凌晨四點,女友帶著 GPU 來我家學習 Kubernetes:打造 K8s AI Infra 的 30 夜1
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言